/*
 * =====================================================================================
 *
 *       Filename:  main.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2019年09月29日 10时42分05秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */

/*
 * 先发现然后选中一个reset即可
 * 只要向目的广播reset命令，然后接受回复消息即可
 */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <syslog.h>
#include <netinet/in.h>
#include <unistd.h>
#include <termio.h>
#include <errno.h>
#include <sys/time.h>
#include <stdarg.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <pthread.h>

#include "list.h"
#include "discovery.h"


int socket_descriptor = -1;
LIST_HEAD(head_dev);
pthread_mutex_t mutex_dev = PTHREAD_MUTEX_INITIALIZER;

typedef struct {
    struct list_head list;
    char ip[16];
    char mac[20];
    char swname[32];
    char software[16];
}discover_dev_t;

unsigned long ds_mcrc32(char *data, int length)
{
	unsigned long crc, poly;
	long crcTable[256];
	int i, j;

	poly = 0x44332211UL;

	for (i=0; i<256; i++) {
		crc = i;
		for (j=8; j>0; j--) {
			if (crc&1) {
				crc = (crc >> 1) ^ poly;
			} else {
				crc >>= 1;
			}
		}
		crcTable[i] = crc;
	}
	crc = 0xFFFFFFFF;

	while( length-- > 0) {
		crc = ((crc>>8) & 0x00FFFFFF) ^ crcTable[ (crc^((char)*(data++))) & 0xFF ];
	}

	return crc^0xFFFFFFFF;
}

/*
 * cmd=serach
 * cmd=reset_passwd mac=ff:ff:ff:ff:ff:ff [passwd=123456]
 * cmd=show_all
 */
void Usage()
{
    printf("cmd=search\n");
    printf("cmd=reset_passwd mac=ff:ff:ff:ff:ff:ff[ passwd=123456]\n");
    printf("cmd=show_all\n\n");
}

int parse_reset_passwd(const char *line, char *mac, char *passwd)
{
    char *_mac, *_passwd, *p;
    _mac = strstr(line, "mac=");
    _passwd = strstr(line, "passwd=");
    if(NULL == _mac) {
        return -1;
    }
    if(_passwd) {
        p = strchr(_mac, ' ');
        if(p) {
            strncpy(mac, _mac + 4, p - (_mac + 4));
        }
        else {
            printf("Must be Something Wrong\n");
        }
        
        p = strchr(_passwd, ' ');
        if(p) {
            strncpy(passwd, _passwd + 7, p - (_passwd + 7));
        }
        else {
            strcpy(passwd, _passwd + 7);
        }
    }
    else {
        p = strchr(_mac, ' ');
        if(p) {
            strncpy(mac, _mac + 4, p - (_mac + 4));
        }
        else {
            strcpy(mac, _mac + 4);
        }
    }
    printf("mac=[%s], passwd=[%s]\n", mac, passwd);
    return 0;
}

 int send_message_for_udp(const char *message,int length)
{
    int vid, type;
    int socket_descriptor; //套接口描述字
    struct sockaddr_in address;//处理网络通信的地址

    socket_descriptor = socket(AF_INET, SOCK_DGRAM, 0);//IPV4 SOCK_DGRAM 数据报套接字（UDP协议）
    if(socket_descriptor < 0) {
        perror("socket()");
        return -1;
    }

    int broadcastEnable = 1;
    int ret=setsockopt(socket_descriptor, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
    if(ret < 0) {
        perror("setsockopt()");
    }

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr("10.10.10.130");
    local.sin_port = htons(0);
    if(bind(socket_descriptor, (struct sockaddr *)&local, sizeof(local)) < 0) {
        perror("bind()");
    }
    
    memset(&address, 0, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(35601);
	address.sin_addr.s_addr = INADDR_BROADCAST;  
	
    ret = sendto(socket_descriptor,message,length,0,(struct sockaddr *)&address,sizeof(address));
    if(ret < 0) {
        perror("sendto()");
    }
    printf("[%s:%d] sendto %d ret is %d\n", __FUNCTION__, __LINE__, length, ret);
        
    close(socket_descriptor);
    
    return 0;
}

void discover_search_send()
{
    find_packet pkts;
    pkts.header.version = 0x01;
    pkts.header.mode = htonl(0x00000000);
    pkts.crc = ds_mcrc32(&pkts, sizeof(pkts) - 4);
    send_message_for_udp(&pkts, sizeof(pkts));
}


void discover_reset_passwd(const char *mac, const char *passwd)
{
    reset_passwd pkts;
    pkts.header.version = 0x01;
    pkts.header.mode = htonl(0x00030002);
    strncpy(pkts.mac, mac, sizeof(pkts.mac));
    pkts.mac_verify[0] = mac[0] ^ mac[1]; 
    pkts.mac_verify[1] = mac[1] ^ mac[2]; 
    pkts.mac_verify[2] = mac[2] ^ mac[3]; 
    pkts.mac_verify[3] = mac[3] ^ mac[4]; 
    pkts.mac_verify[4] = mac[4] ^ mac[5]; 
    pkts.mac_verify[5] = mac[5] ^ mac[6]; 
    pkts.mac_verify[6] = mac[6] ^ mac[7]; 
    pkts.mac_verify[7] = mac[3] ^ mac[7]; 
    if(passwd[0]) {
        strncpy(pkts.new_passwd, passwd, sizeof(pkts.new_passwd));
    }
    else {
        memset(pkts.new_passwd, 0, sizeof(pkts.new_passwd));
    }
    pkts.crc = ds_mcrc32(&pkts, sizeof(pkts) - 4);
    send_message_for_udp(&pkts, sizeof(pkts));
}

void show_all_devices()
{
    struct list_head *pos;
    pthread_mutex_lock(&mutex_dev);
    list_for_each(pos, &head_dev) {
        discover_dev_t *dev = list_entry(pos, discover_dev_t, list);
        printf("ip=%s, mac=%s, software=%s\n", dev->ip, dev->mac, dev->software);
    }
    pthread_mutex_unlock(&mutex_dev);
}

void thread_send(void *arg)
{
    pthread_detach(pthread_self());
    while(1) {
        /* 标准输入读进命令 */
        char line[512] = {0};
        char *rd = fgets(line, sizeof(line) - 1, stdin);
        if(NULL == rd) {
            perror("fgets()");
            continue;
        }
        int i = 0;
        for(i = 0; i < strlen(line); i++) {
            if(line[i] == '\n') {
                line[i] = '\0';
            }
        }
        printf("line=[%s]\n", line);
        
        if(0 == strncmp(line, "cmd=search", strlen("cmd=search"))) {
            discover_search_send();
            usleep(1000 * 100);
            discover_search_send();
            usleep(1000 * 100);
            discover_search_send();
        }
        else if(0 == strncmp(line, "cmd=reset_passwd", strlen("cmd=reset_passwd"))) {
            char mac[24] = {0};
            char passwd[33] = {0};
            if(parse_reset_passwd(line, mac, passwd) < 0) {
                Usage();
                continue;
            }
            discover_reset_passwd(mac, passwd);
            usleep(1000 * 100);
            discover_reset_passwd(mac, passwd);
            usleep(1000 * 100);
            discover_reset_passwd(mac, passwd);
        }
        else if(0 == strncmp(line, "cmd=show_all", strlen("cmd=show_all"))) {
            show_all_devices();
        }
        else {
            Usage();
        }
    }
    pthread_exit(NULL);
}

discover_dev_t *discovery_dev_get_by_ip(const char *ip)
{
    struct list_head *pos;
    pthread_mutex_lock(&mutex_dev);
    list_for_each(pos, &head_dev) {
        discover_dev_t *dev = list_entry(pos, discover_dev_t, list);
        if(strcmp(ip, dev->ip) == 0) {
            pthread_mutex_unlock(&mutex_dev);
            return dev;
        }
    }
    pthread_mutex_unlock(&mutex_dev);
    return NULL;
}

int discovery_dev_add(const char *ip, const char *mac, const char *swname, const char *software)
{
    discover_dev_t *dev_by_ip = discovery_dev_get_by_ip(ip);
    if(dev_by_ip) {
        //printf("Device ip=%s, mac=%s, swname=%s, software=%s Already In List.\n", 
        //        dev_by_ip->ip, dev_by_ip->mac, dev_by_ip->swname, dev_by_ip->software);
        return 0;
    }
    discover_dev_t *dev = (discover_dev_t *)calloc(1, sizeof(*dev));
    if(NULL == dev) {
        perror("calloc()");
        return -1;
    }
    strncpy(dev->ip, ip, sizeof(dev->ip));
    strncpy(dev->mac, mac, sizeof(dev->mac));
    strncpy(dev->software, software, sizeof(dev->software));
    printf("Device ip=%s, mac=%s, swname=%s, software=%s Add To List\n",
            dev->ip, dev->mac, dev->swname, dev->software);
    pthread_mutex_lock(&mutex_dev);
    list_add_tail(&(dev->list), &head_dev);
    pthread_mutex_unlock(&mutex_dev);
    return 0;
}

int main(int argc, char **argv)
{
    struct sockaddr_in sin;
    fd_set allset, rset;
    
    bzero(&sin,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("0.0.0.0");
    sin.sin_port = htons(35602);
    socklen_t sin_len = sizeof(sin);
    
    socket_descriptor = socket(AF_INET,SOCK_DGRAM,0);
    if(socket_descriptor < 0) {
        perror("socket()");
        return -1;
    }
    if(bind(socket_descriptor,(struct sockaddr *)&sin,sizeof(sin)) < 0) {
        perror("bind()");
        close(socket_descriptor);
        return -1;
    }

    pthread_t th;
    if(0 != pthread_create(&th, NULL, (void *)&thread_send, NULL)) {
        perror("pthread_create()");
        exit(0);
    }

    while(1) {
	    FD_ZERO(&rset);
        FD_SET(socket_descriptor, &rset);
	    int num =  select(socket_descriptor + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, NULL);//阻塞
        if(num < 0) {
            perror("select");
            continue;
        }
            
        if(!FD_ISSET(socket_descriptor, &rset)) {
            printf("must be something wrong\n");
        }

        char message[1024]  = {0};
		int rec_len = recvfrom(socket_descriptor, message,sizeof(message), 0 ,(struct sockaddr *)&sin,&sin_len);
        if(rec_len < 0) {
            perror("recvfrom()");
            continue;
        }
        else if(rec_len == 0) {
            printf("UDP Message has empty body\n");
            continue;
        }
		header_packet *header = (header_packet *)message;
		printf("version: %d mode:%08x lens:%d\n", header->version, ntohl(header->mode), rec_len);
        switch(ntohl(header->mode)) {
        case 0x00000001: /* 发现应答 */
            {
                find_ack_packet *ack_packet = (find_ack_packet *)message;
		        uint32_t crc, pkts_crc = (message[rec_len-4] & 0xff) |
                                         (message[rec_len-3] & 0xff)<<8 | 
                                         (message[rec_len-2] & 0xff)<<16 | 
                                         (message[rec_len-1] & 0xff)<<24;
		        crc = ds_mcrc32(message, rec_len-4);
		        printf("crc:%08x(my crc:%08x)", pkts_crc, crc);
                if(crc == pkts_crc) {
                    //保存节点
                    discovery_dev_add(ack_packet->ip, ack_packet->mac, ack_packet->swname, ack_packet->software);
                }
            }
            break;
        case 0x00030003: /* 密码重置应答 */
            {
                //根据最新版本来做啊!!!
                reset_passwd_ack *pkts = (reset_passwd_ack *)message;
		        uint32_t crc, pkts_crc = (message[rec_len-4] & 0xff) |
                                         (message[rec_len-3] & 0xff)<<8 | 
                                         (message[rec_len-2] & 0xff)<<16 | 
                                         (message[rec_len-1] & 0xff)<<24;
		        crc = ds_mcrc32(message, rec_len-4);
		        printf("crc:%08x(my crc:%08x)", pkts_crc, crc);
                if(crc == pkts_crc) {
                    printf("Device mac=%s Reset Password OK\n", pkts->mac);
                }
            }
            break;
        default:
            printf("do not process message %08X\n", ntohl(header->mode));
            break;
        }
    }
    return 0;
}



